/*
 *  This file is part of LifeBudget - Copyright 2010 Clifton Eaton
 *  ------------------------------------------------------------------
 *  LifeBudget is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  LifeBudget is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with LifeBudget.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.lifebudget.app.persistence.repository;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;

import org.lifebudget.app.client.mvc.model.Category;
import org.lifebudget.app.misc.UserHelper;
import org.lifebudget.app.persistence.EMF;
import org.lifebudget.app.persistence.dao.CategoryDAO;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

/**
 * The Class CategoryRepository uses Data Access Objects and persists
 * them using JPA.  
 */
public class CategoryRepository {
	
	/** The emf. */
	private EntityManagerFactory emf;

	/**
	 * Instantiates a new category repository.
	 */
	public CategoryRepository() {
		this.emf = EMF.get();
	}

	/**
	 * Update categories.
	 *
	 * @param categories the categories
	 * @return the list
	 */
	public List<Category> updateCategories(List<Category> categories) {
		List<Category> updates = new ArrayList<Category>();
		for (int i = 0; i < categories.size(); i++) {
			Category category = categories.get(i);
			if (category.getId() == 0) {
				// adds.add(category);
				long newId = addCategory(category);
				category.setId(newId);
			} else {
				updates.add(category);
			}
		}
		if (updates.size() > 0) {
			EntityManager em = emf.createEntityManager();
			try {
				for (Category c : updates) {
					Key key = KeyFactory.createKey(CategoryDAO.class.getSimpleName(), c.getId());
					CategoryDAO pc = em.find(CategoryDAO.class, key);
					if (pc != null) {
						pc.setFieldsFromModelObject(c);
					}
				}
			} finally {
				em.close();
			}
		}
		return categories;
	}

	/**
	 * Adds the category.
	 *
	 * @param category the category
	 * @return the long
	 */
	public long addCategory(Category category) {
		EntityManager em = emf.createEntityManager();
		CategoryDAO pc;
		try {
			pc = new CategoryDAO(category);
			pc.setUser(UserHelper.getUser().getUserId());
			em.persist(pc);
			em.refresh(pc);
		} finally {
			em.close();
		}
		return pc.getKey().getId();
	}

	/**
	 * Update category.
	 *
	 * @param category the category
	 */
	public void updateCategory(Category category) {
		EntityManager em = emf.createEntityManager();
		try {
			if (category.getId() == 0) {
				CategoryDAO pc = new CategoryDAO(category);
				pc.setUser(UserHelper.getUser().getUserId());
				em.persist(pc);
			} else {
				Key key = KeyFactory.createKey(CategoryDAO.class.getSimpleName(), category.getId());
				CategoryDAO pc = em.find(CategoryDAO.class, key);
				if (pc != null) {
					pc.setFieldsFromModelObject(category);
				}
			}

		} finally {
			em.close();
		}
	}

	/**
	 * Gets the all categories.
	 *
	 * @param includeDefault the include default
	 * @return the all categories
	 */
	public List<Category> getAllCategories(boolean includeDefault) {
		List<Category> list = new ArrayList<Category>();
		EntityManager em = emf.createEntityManager();
		Query q;
		if (includeDefault) {
			q = em.createQuery("SELECT c FROM " + CategoryDAO.class.getName()
					+ " c WHERE c.user = ?1 AND c.archived = false");
		} else {
			q = em.createQuery("SELECT c FROM " + CategoryDAO.class.getName()
					+ " c WHERE c.user = ?1 AND c.archived = false AND c.defaultCategory = false");
		}
		q.setParameter(1, UserHelper.getUser().getUserId());
		try {
			List<CategoryDAO> results = q.getResultList();

			for (CategoryDAO p : results) {
				list.add(p.toModelObject());
			}

		} finally {
			em.close();
		}
		// list = TestData.getCategories();
		return list;
	}

	/**
	 * Find category.
	 *
	 * @param id the id
	 * @return the category
	 */
	public Category findCategory(long id) {
		EntityManager em = emf.createEntityManager();
		Category category = null;
		try {
			Key categoryKey = KeyFactory.createKey(CategoryDAO.class.getSimpleName(), id);
			CategoryDAO pCategory = em.find(CategoryDAO.class, categoryKey);
			if (pCategory != null) {
				category = pCategory.toModelObject();
			}
		} finally {
			em.close();
		}
		return category;
	}

	/**
	 * Removes the all persistable categories.
	 */
	private void removeAllPersistableCategories() {
		List<CategoryDAO> list = new ArrayList<CategoryDAO>();

		EntityManager em = emf.createEntityManager();
		Query q = em.createQuery("SELECT c FROM " + CategoryDAO.class.getName() + " c WHERE c.user = ?1");
		q.setParameter(1, UserHelper.getUser().getUserId());
		try {
			list = q.getResultList();

			for (CategoryDAO c : list) {

				em.remove(c);
			}
		} finally {
			em.close();
		}
	}

	/**
	 * Gets the test categories.
	 *
	 * @return the test categories
	 */
	private static List<CategoryDAO> getTestCategories() {
		List<CategoryDAO> cats = new ArrayList<CategoryDAO>();
		for (int i = 0; i < 5; i++) {
			CategoryDAO c = new CategoryDAO("Category " + i,
					"This is a special category for capturing expenses related to " + i);
			c.setUser(UserHelper.getUser().getUserId());
			cats.add(c);

		}
		return cats;
	}

	/**
	 * Gets the default category id.
	 *
	 * @return the default category id
	 */
	public long getDefaultCategoryId() {
		long defaultId = 0;
		EntityManager em = emf.createEntityManager();
		Query q;
		q = em.createQuery("SELECT c FROM " + CategoryDAO.class.getName()
				+ " c WHERE c.user = ?1 AND c.defaultCategory = true");

		q.setParameter(1, UserHelper.getUser().getUserId());
		try {
			List<CategoryDAO> results = q.getResultList();
			if (!results.isEmpty()) {
				// only expecting one result
				defaultId = results.get(0).getKey().getId();
			}
		} finally {
			em.close();
		}
		return defaultId;
	}

	/**
	 * Gets the category.
	 *
	 * @param id the id
	 * @return the category
	 */
	public Category getCategory(long id) {
		Category c = null;
		EntityManager em = emf.createEntityManager();
		try {
			Key key = KeyFactory.createKey(CategoryDAO.class.getSimpleName(), id);
			CategoryDAO pc = em.find(CategoryDAO.class, key);
			if (pc != null) {
				c = pc.toModelObject();
			}

		} finally {
			em.close();
		}
		return c;
	}

}
